#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<pthread.h>

using namespace std;

#define TNUM 4    // 多线程个数
// 函数的执行方法，不同的执行方法  函数指针
typedef void (*func_t)(const string& name, pthread_mutex_t* pmtx, pthread_cond_t* pcond); 

volatile bool quit = false; // 设置一个退出的标记位

// 定义全局的锁和控制变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

class ThreadData
{
public:
    ThreadData (const string& name, func_t func, pthread_mutex_t* pmtx, pthread_cond_t* pcond)
        :name_(name)
        ,func_(func)
        ,pmtx_(pmtx)
        ,pcond_(pcond)
    {}
public:
    string name_;
    func_t func_;
    pthread_mutex_t* pmtx_;
    pthread_cond_t* pcond_;
};

void func1(const string& name, pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{
    while (!quit)
    {
        // 线程等待                                阻塞: 相当于把该线程从R状态变成S态  并放到等待队列中
        // 一定要在加锁和解锁之间wait！！
        pthread_mutex_lock(pmtx);
        //if (临界资源是否就绪 -- 否) pthread_cond_wait  资源就绪了，主线程在唤醒它
        pthread_cond_wait(pcond, pmtx);   // 默认该线程在执行的时候，wait代码被执行，当前线程立即被阻塞
        cout << name << " running —— a ———— 播放" << endl;
        pthread_mutex_unlock(pmtx);
    }
}

void func2(const string& name, pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{
    while (!quit)
    {
        // 线程等待                                阻塞: 相当于把该线程从R状态变成S态  并放到等待队列中
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcond, pmtx);   // 默认该线程在执行的时候，wait代码被执行，当前线程立即被阻塞
        cout << name << " running —— b ———— 下载" << endl;
        pthread_mutex_unlock(pmtx);
    }
}

void func3(const string& name, pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{
    while (!quit)
    {
        // 线程等待                                阻塞: 相当于把该线程从R状态变成S态  并放到等待队列中
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcond, pmtx);   // 默认该线程在执行的时候，wait代码被执行，当前线程立即被阻塞
        cout << name << " running —— c ———— 扫描" << endl;
        pthread_mutex_unlock(pmtx);
    }
}

void func4(const string& name, pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{
    while (!quit)
    {
        // 线程等待                                阻塞: 相当于把该线程从R状态变成S态  并放到等待队列中
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcond, pmtx);   // 默认该线程在执行的时候，wait代码被执行，当前线程立即被阻塞
        cout << name << " running —— d ———— 打印" << endl;
        pthread_mutex_unlock(pmtx);
    }
}

void* Entry(void* args)
{
    ThreadData* td = (ThreadData*)args;  // td在每个线程私有的栈空间中保存
    td->func_(td->name_, td->pmtx_, td->pcond_);   // 它是一个函数,调用完成会返回
    delete td;  // 释放空间

    return nullptr;
}

int main()
{
    pthread_mutex_t mtx; // 定义锁
    pthread_cond_t cond; // 定义条件变量

    // 初始化锁和条件变量
    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);

    // 创建线程id
    pthread_t tid[TNUM];
    
    // 创建不同的执行方法
    func_t funcs[TNUM] = {func1, func2, func3, func4};

    // 创建多线程
    for (int i = 0; i < TNUM; i++)
    {
        // 线程名称
        string name = "Thread ";
        name += to_string(i + 1);
        // 初始化类，内容为线程名称和执行方法
        ThreadData* td = new ThreadData(name, funcs[i], &mtx, &cond);
        // 创建线程，传递参数
        pthread_create(tid + i, nullptr, Entry, (void*)td);
    }

    sleep(3); // 休息3秒，看一下，线程确实被阻塞了

    // 控制新线程   ctrl new thread
    int cnt = 10;
    while (cnt)
    {
        cout << "resume thread run code ..." << cnt-- << endl;
        pthread_cond_signal(&cond);  // 去唤醒线程  唤醒哪一个呢？ 不确定，取决于调度器  一个一个唤醒
        // pthread_cond_broadcast(&cond); // 唤醒线程，全部唤醒
        sleep(1);
    }

    cout << "ctrl done .." << endl;
    quit = true;  // 退出
    pthread_cond_broadcast(&cond);

    // 线程回收
    for (int i = 0; i < TNUM; i++)
    {
        pthread_join(tid[i], nullptr);
        cout << "thread " << tid[i] << " " << "quit" <<endl;
    }

    // 销毁锁和条件变量
    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);

    return 0;
}